package com.hanock.sm.fork2d;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;

public class Canvas {
	
	// NOTE: Textures are marked when drawn.

	/**
	 *  (left, bottom) -> (left, top) -> (right, bottom) -> (right, top)
	 */
	private ByteBuffer squareVertexBuffer;
	
	/**
	 *  (left, bottom) -> (left, top) -> (right, bottom) -> (right, top)
	 */
	private ByteBuffer textureBuffer;
	
	private ByteBuffer lineVertexBuffer;
	
	private ByteBuffer circleVertexBuffer;
	
	private GL10 gl;
	private ViewportInfo viewportInfo;
	private TextureManager textureManager;
	
	private RectF tempRectF;
	
	
	
	public Canvas() {
		
		//
		squareVertexBuffer = ByteBuffer.allocateDirect(4 * 3 * 4);
		squareVertexBuffer.order(ByteOrder.nativeOrder());
		
		//
		circleVertexBuffer = ByteBuffer.allocateDirect(362 * 3 * 4);
		circleVertexBuffer.order(ByteOrder.nativeOrder());
		
		//
		lineVertexBuffer = ByteBuffer.allocateDirect(2 * 3 * 4);
		lineVertexBuffer.order(ByteOrder.nativeOrder());
		
		//
		textureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4);
		textureBuffer.order(ByteOrder.nativeOrder());
		
		textureBuffer.putFloat(0);
		textureBuffer.putFloat(1);
		
		textureBuffer.putFloat(0);
		textureBuffer.putFloat(0);
		
		textureBuffer.putFloat(1);
		textureBuffer.putFloat(1);
		
		textureBuffer.putFloat(1);
		textureBuffer.putFloat(0);
		
		textureBuffer.position(0);
		
		//
		tempRectF = new RectF();
		textureManager = TextureManager.getInstance();
	}
	
	public void drawRect(Rect rect){
		drawRect(rect.left, rect.top, rect.right, rect.bottom);
	}
	
	public void drawRect(RectF rect){
		drawRect(rect.left, rect.top, rect.right, rect.bottom);
	}
	
	public void drawRect(float left, float top, float right, float bottom){
		
		// set vertices
		squareVertexBuffer.position(0);
		
		squareVertexBuffer.putFloat(left);
		squareVertexBuffer.putFloat(bottom);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.putFloat(left);
		squareVertexBuffer.putFloat(top);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.putFloat(right);
		squareVertexBuffer.putFloat(bottom);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.putFloat(right);
		squareVertexBuffer.putFloat(top);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.position(0);
		
		//
		gl.glDisable(GL10.GL_TEXTURE_2D);
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
	
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, squareVertexBuffer);
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
		
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		
		gl.glEnable(GL10.GL_TEXTURE_2D);
	}

	public void drawImage(Rect srcRect, Rect desRect, int resId){
		Texture texture = textureManager.load(resId);
		drawImage(srcRect, desRect, texture);
	}
	
	public void drawImage(Rect srcRect, RectF desRect, int resId){
		Texture texture = textureManager.load(resId);
		drawImage(srcRect, desRect, texture);
	}
	
	public void drawImage(Rect srcRect, Rect desRect, Texture texture){
		
		tempRectF.left = desRect.left;
		tempRectF.right = desRect.right;
		tempRectF.top = desRect.top;
		tempRectF.bottom = desRect.bottom;
		
		drawImage(srcRect, tempRectF, texture);
	}
	
	public void drawImage(Rect srcRect, RectF desRect, Texture texture){
		
		if (texture == null)
			return;

		// set rect in openGL coordinate system
		float left = desRect.left;
		float right =  desRect.right;
		float top = desRect.top;
		float bottom = desRect.bottom;
		
		// set vertices
		squareVertexBuffer.position(0);
		
		squareVertexBuffer.putFloat(left);
		squareVertexBuffer.putFloat(bottom);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.putFloat(left);
		squareVertexBuffer.putFloat(top);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.putFloat(right);
		squareVertexBuffer.putFloat(bottom);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.putFloat(right);
		squareVertexBuffer.putFloat(top);
		squareVertexBuffer.putFloat(0);
		
		squareVertexBuffer.position(0);
		
		// set texture coordinates
		textureBuffer.position(0);
		if (srcRect == null)
			texture.getTextureCoordinate(textureBuffer);
		else
			texture.getTextureCoordinate(srcRect, textureBuffer);

		
		//
		gl.glBindTexture(GL10.GL_TEXTURE_2D, texture.getTextureId());
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, squareVertexBuffer);
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
				
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
		
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	}
	
	public void drawLine(float x1, float y1, float x2, float y2, int width){
		
		// set line buffer
		lineVertexBuffer.position(0);
		
		lineVertexBuffer.putFloat(x1);
		lineVertexBuffer.putFloat(y1);
		lineVertexBuffer.putFloat(0);
		
		lineVertexBuffer.putFloat(x2);
		lineVertexBuffer.putFloat(y2);
		lineVertexBuffer.putFloat(0);
		
		lineVertexBuffer.position(0);
		
		
		// draw line
		gl.glLineWidth(width);
		
		gl.glDisable(GL10.GL_TEXTURE_2D);
		gl.glDisable(GL10.GL_BLEND);
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVertexBuffer);
		gl.glDrawArrays(GL10.GL_LINES, 0, 2);
		
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		
		gl.glEnable(GL10.GL_BLEND);
		gl.glEnable(GL10.GL_TEXTURE_2D);
	}
	
	public void drawArc(float x, float y, float r){
		drawArc(x, y, r, 0, 360);
	}
	
	public void drawArc(float x, float y, float r, int startAngle, int sweepAngle){
		
		tempRectF.left = x - r;
		tempRectF.right = x + r;
		tempRectF.top = y + r;
		tempRectF.bottom = y - r;
		
		drawArc(tempRectF, startAngle, sweepAngle);
	}
	
	public void drawArc(RectF oval){
		drawArc(oval, 0, 360);
	}
	
	public void drawArc(RectF oval, int startAngle, int sweepAngle){
		
		//
		float x = (oval.left + oval.right) / 2;
		float y = (oval.top + oval.bottom) / 2;
		float xr = (oval.right - oval.left) / 2;
		float yr = (oval.top - oval.bottom) / 2;
		
		//
		if (sweepAngle <= 0)
			sweepAngle = 0;
		
		if (sweepAngle > 360)
			sweepAngle = 360;

		//
		circleVertexBuffer.position(0);
		
		circleVertexBuffer.putFloat(0);
		circleVertexBuffer.putFloat(0);
		circleVertexBuffer.putFloat(0);
		
		for (int i = 0; i <= sweepAngle; i++){
			
			double theta = (-i + startAngle)* Math.PI / 180;
			
			circleVertexBuffer.putFloat(xr*(float)Math.cos(theta));
			circleVertexBuffer.putFloat(yr*(float)Math.sin(theta));
			circleVertexBuffer.putFloat(0);
		}
		
		circleVertexBuffer.position(0);
		
		//
		gl.glPushMatrix();
		gl.glTranslatef(x, y, 0);	
		
		//
		gl.glDisable(GL10.GL_TEXTURE_2D);
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
	
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, circleVertexBuffer);
		gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, sweepAngle + 2);
		
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		
		gl.glEnable(GL10.GL_TEXTURE_2D);
		
		//
		gl.glPopMatrix();
		
	}
	
	/**
	 * @param height used to convert coordinate between opengl and the custom coordinate system which uses opposite y-axis
	 */
	public void beforeDraw(GL10 gl, ViewportInfo viewportInfo){
		this.gl = gl;
		this.viewportInfo = viewportInfo;
	}

	public void afterDraw(){
		this.gl = null;
		this.viewportInfo = null;
	}

	public ViewportInfo getViewportInfo() {
		return viewportInfo;
	}
	
	public GL10 getGL() {
		return gl;
	}
}





